可以这样类比来想:think of it like you phone is slowing down after you install too many apps, you need to find out which app is using the resourses and is making you phone slow.
1、we can use chrome devtools, and in the performance tab, and see what is taking the most time.
2、we can use react devtools chrome extension, we can track unneccessary rerenders or infinite loop of rerenders.
3、we can use React.memo and useCallback to making app fast and tracking where is slow.
4、I will check if there are any large amount of data to render at once. usually we render only the visible amount of data, use packages like react.window or react.virtulized.
5、avoid inline functions and objects。
Inline functions and objects are recreated on every render, which can cause child components to re-render unnecessarily — especially if those children depend on referential equality (React.memo, dependency arrays, etc.).
Example problem:
xxxxxxxxxx<Child onClick={() => handleClick()} data={{ foo: 1 }} />Every render:
() => handleClick()is a new function{ foo: 1 }is a new objectSo
Childthinks props changed → re-renders every time, even if nothing actually changed.How to fix it:
①Use
useCallbackfor functionsxxxxxxxxxxconst handleClick = useCallback(() => {// logic}, []);②Use
useMemofor objects/arraysxxxxxxxxxxconst data = useMemo(() => ({ foo: 1 }), []);
上面这个说法过时了,avoid inline functions and objects说的太绝对了。但我还是把它保留,因为这个说法很流行。
在现代 React 开发中,“绝对禁止内联函数”被认为是一种过度优化(Over-optimization)。面试时,如果你能辩证地看待这个问题,会显得你更有经验。
以下是针对这个问题的深度解析,以及如何在面试中“专业地反驳”或补充:
- 为什么会有“避免内联函数”的说法?
这个观点的核心担忧是 引用相等性(Reference Equality):
- 在 JavaScript 中,
() => {} === () => {}是false。- 如果父组件重新渲染,内联函数会被重新创建,导致传给子组件的 Props 引用发生变化。
- 如果子组件使用了
React.memo,它会误以为数据变了,从而触发重新渲染。
- 为什么这种说法“不准确”?
- 原生标签无所谓:对于
<button>、<div>、<input>这些原生标签,创建内联函数的开销极小,且它们本身不具备memo逻辑,内联函数不会导致任何额外的性能损失。- React 渲染非常快:在大多数中小型组件树中,重新渲染的开销远小于过度使用
useCallback带来的代码维护成本。- useCallback 也有开销:
useCallback本身也要执行逻辑、定义函数并维护依赖数组。如果盲目使用,反而会让代码变得臃肿且性能提升微乎其微。
- 面试时的“标准标准回答” (The "Senior" Answer)
当面试官提到“避免内联函数”时,你可以这样回答:
中文逻辑:
“‘避免内联函数’这个原则在 React 中需要视情况而定。
它的主要目的是为了保持引用稳定。如果一个内联函数被传递给了包裹了
React.memo的子组件,或者作为useEffect的依赖项,那么内联函数确实会导致不必要的重绘或 Effect 的重复执行。但是,对于原生 HTML 标签,或者没有性能瓶颈的普通组件,内联函数是完全可以接受的。过度使用
useCallback反而会增加代码复杂度。所以我的原则是:先编写简洁的内联代码,只有在 Profiler 检测到性能瓶颈,或者需要保持引用一致性时,才去使用
useCallback优化。”
- 英文面试版本 (Pro Interview Version)
"The advice to avoid inline functions is not an absolute rule; it's about managing reference stability.
The real issue isn't the creation of the function itself—React handles that very efficiently. The issue is that a new function reference can break memoization in child components (using
React.memo) or trigger unnecessaryuseEffectcalls.If I'm passing a function to a native element like a
<button>, an inline function is perfectly fine. However, if I'm passing it to a heavy, memoized component, I would wrap it inuseCallbackto ensure the child doesn't re-render unless its actual logic changes.My philosophy is: Don't prematurely optimize. I prefer clean, readable code with inline functions first, and only optimize when the Profiler shows a real performance bottleneck."
上面的步骤要理解,然后简要的答案先说出来:
When my React app becomes slow after adding new features, I usually debug it in a few steps.
First, I try to measure the problem. I use React DevTools Profiler to see which components are re-rendering too often or taking too long to render.
Then I look for the common cause, which is unnecessary re-renders. For example, sometimes new features introduce inline functions or objects in props, which causes child components to re-render even if they are wrapped with
React.memo.In those cases, I might use
useCallbackoruseMemoto keep the references stable. But I don’t just add them everywhere — I only use them when I really see a performance issue.
After that, I also check the component structure. Sometimes the problem is that the state is placed too high in the tree, so a small change causes a lot of components to re-render. In that case, I try to move state closer to where it’s used, or split components to isolate updates.
For UI-heavy cases, like big lists, I use techniques like windowing or virtualization to only render what’s visible on the screen. And sometimes I use code splitting to reduce the initial load.
Overall, I try to find the real cause first, and then apply the simplest fix instead of over-optimizing.
The general idea is:If the user is already authenticated, redirect them away from the login page.
You check the auth state, and if the user is logged in, redirect them:
xxxxxxxxxximport { Navigate } from "react-router-dom";import { useAuth } from "./authContext";function LoginPage() { const { user } = useAuth(); if (user) { return <Navigate to="/dashboard" replace />; } return <LoginForm />;}export default LoginPage;Explanation:
user exists → redirect to dashboardThis ensures the login page is never visible to authenticated users.
Sometimes I create a wrapper:
xxxxxxxxxxfunction PublicOnlyRoute({ children }) { const { user } = useAuth(); return user ? <Navigate to="/dashboard" replace /> : children;}Then wrap the route:
xxxxxxxxxx<Route path="/login" element={ <PublicOnlyRoute> <LoginPage /> </PublicOnlyRoute> }/>If the backend renders the page, the server can also redirect if the session exists.
理解上面的内容后,简要回答:
I prevent the login page from being accessible after login by checking whether the user is authenticated. If the user is already logged in, I immediately redirect them to a protected page like the dashboard. In React Router, this usually means returning a
<Navigate>component whenuserexists. This ensures the login page is only visible to unauthenticated users.
Users should never see raw server errors like “500 Internal Server Error”.
Example:
This keeps the UI clean and understandable.
In React components or services:
xxxxxxxxxxtry { const res = await fetch("/api/data"); if (!res.ok) throw new Error("Request failed"); return await res.json();} catch (err) { setError(err.message);}Then the UI reacts to that error state.
For example:
xxxxxxxxxx{error && ( <div> <p>{error}</p> <button onClick={retry}>Try Again</button> </div>)}I often show different messages depending on the error:
This makes the UI feel professional and predictable.
React Query makes this much easier:
xxxxxxxxxxuseQuery({ queryKey: ["data"], queryFn: fetchData, onError: (err) => { toast.error(err.message); }});React Query automatically gives:
error stateisError booleanSo the UI can handle errors more gracefully.
理解上面之后,简要的回答:
I usually handle API errors in a few simple ways.
First, I wrap API calls in
try/catch, or I use tools like React Query, which already gives meerrorandisErrorstates.When an error happens, I store it in state and show a friendly message in the UI. For example, I might show “Something went wrong” or display a toast message. I can also give users a retry button so they can try again.
For different HTTP errors, I handle them differently. For example, if it’s 401, I redirect the user to the login page. If it’s 403, I show an “access denied” message. If it’s a validation error like 400, I show the message near the form field.
For serious cases, I also use an error boundary in React, so if a component crashes, it won’t break the whole page.
And for temporary network issues, I usually just retry the request a few times automatically, or let the user click retry manually.
I also log errors so I can debug them later.
useEffect dependency arrayOne of the most common causes is a missing or incorrect dependency array:
❌ Bad (fetch runs every render)
xxxxxxxxxxuseEffect(() => { fetchData();});✅ Good (runs only once on mount)
xxxxxxxxxxuseEffect(() => { fetchData();}, []);Or if dependencies are required, ensure they don’t change on every render.
useCallbackIf the fetch function is declared inline and passed to children or used inside effects, it may be recreated every render, causing repeated calls.
❌ Bad
xxxxxxxxxxuseEffect(() => { fetchData();}, [fetchData]);const fetchData = () => { }; // new function per render✅ Good
xxxxxxxxxxconst fetchData = useCallback(() => { // call API}, []);useMemoIf parameters for your fetch are created inline (like an object), the reference changes every time:
❌
xxxxxxxxxxuseEffect(() => { fetchData(params);}, [{ id: 1 }]);✔️
xxxxxxxxxxconst params = useMemo(() => ({ id: 1 }), []);Sometimes multiple sibling components fetch the same data.
Solution:
This way the data is fetched once and shared.
Tools like React Query automatically:
React Query example:
xxxxxxxxxxconst { data } = useQuery({ queryKey: ["user", id], queryFn: fetchUser,});Duplicate calls are automatically prevented.
Some developers accidentally remount a component repeatedly:
xxxxxxxxxx{isOpen && <UserList />}If isOpen toggles repeatedly, the component unmounts and remounts, triggering useEffect again.
Fix:
理解上面的内容后,简要的回答:
First, I check the
useEffectdependency array — missing or unstable dependencies often cause repeated fetch calls. Then I make sure the fetch function is memoized withuseCallbackand any params withuseMemo, so they don’t change every render. If several components need the same data, I move the fetching logic into a global store, such as zustand, so the data is fetched once and reused everywhere. For even more control, I use libraries like React Query, which provide request deduplication, caching, and stale-while-revalidate behavior out of the box.
In short: stabilize dependencies, avoid remounting, and centralize or cache the data so it only gets fetched once.
可以这样想:think of this like you are travelling, you don't take your complete cupboard or wardrobe with you, you take neccessary some part of it in the travel.
Just like that, whenever you writing thousands of files in your app, you don't need to ship everything to the production.So you need to bundle it and organize it somehow so that it will take minimum size on the user's device and it will be efficient for the performance.
理解了上面的内容之后,简要回答:
I usually focus on three things to optimize bundle size: loading less code, removing unnecessary dependencies, and checking the bundle regularly.
First, I use code splitting. For example, I split routes using
React.lazyandSuspense, so users only load the code for the page they are actually visiting. For heavier features, I also use dynamic imports so they are loaded only when needed.
Then I look at dependencies. I use tools like Webpack Bundle Analyzer or Vite bundle analysis to see what is taking up space. If I find a big library that is not fully needed, I either replace it with a lighter one or import only the parts I use instead of the whole package.
After that, I make sure the build is optimized. Things like tree-shaking, minification, and removing dead code are usually handled by the build tool, but I double-check that everything is working correctly and I’m not accidentally importing large modules.
For static assets, I also try to keep them lightweight. For example, I prefer SVGs over PNGs when possible, and I use CDN or compression for images.
Overall, my approach is pretty simple: split what can be lazy-loaded, remove what I don’t need, and keep checking the bundle size over time.
当面试官问large forms,他们想要看到你是否能够优化form、维持状态的能力。
这是Shubham Kulkarni | Coding and Interviews这位老师的回答要点。
1、use form management library like react hook form,
2、structure the form to logical steps or sections.
3、use controlled components,
4、use zod library to validate.
下面是chatgpt的回答:
✅ 1. Group related fields into logical sections
Don’t treat 20 fields as 20 individual states. Instead, think of your form as sections:
This gives you:
✔ Better UI/UX ✔ Smaller components ✔ Cleaner validation
Example structure:
xxxxxxxxxx<FormProvider {methods}> <PersonalInfoSection /> <ContactSection /> <PreferencesSection /></FormProvider>✅ 2. Use a form library — don’t manage 20 states manually
It handles:
For 20+ fields, RHF is definitely better than useState or even Formik.
Example:
xxxxxxxxxxconst methods = useForm({ defaultValues: { 20 fields here }, mode: "onSubmit",});✅ 3. Store all form values in one place (not 20 states)
For large forms, do NOT use:
xxxxxxxxxxconst [name, setName] = useState('');const [email, setEmail] = useState('');Instead:
xxxxxxxxxxconst methods = useForm({ defaultValues: { name: '', email: '', age: '', // ... }});Or if using useState manually:
xxxxxxxxxxconst [form, setForm] = useState({});✅ 4. Extract repeating field logic into reusable components
If many fields share:
Create a component:
xxxxxxxxxxfunction FormInput({ name, label, register, errors }) { return ( <div> <label>{label}</label> <input {register(name)} /> {errors[name] && <p>{errors[name].message}</p>} </div> );}Now your form becomes:
xxxxxxxxxx<FormInput name="email" label="Email" /><FormInput name="phone" label="Phone" /><FormInput name="age" label="Age" />✅ 5. Use zod to avoid manual checks
With 20+ fields, writing if (!name) error... everywhere becomes painful.
Use Zod:
xxxxxxxxxxconst schema = z.object({ name: z.string().min(1), email: z.string().email(), phone: z.string().min(10), // 20 more...});Integrate with RHF:
xxxxxxxxxxconst methods = useForm({ resolver: zodResolver(schema)});Now all validation is centralized and reusable.
✅ 6. Use dynamic generation (optional but powerful)
If fields share patterns, define them as config:
xxxxxxxxxxconst fieldConfig = [ { name: "email", label: "Email" }, { name: "phone", label: "Phone" }, { name: "address", label: "Address" }, // ...];Render automatically:
xxxxxxxxxx{fieldConfig.map((f) => ( <FormInput key={f.name} {f} />))}Great for 20–100 fields.
✅ 7. Split the form into tabs or steps if possible
For UX improvement:
Use a stepper or wizard.
Better:
✔ Less intimidating ✔ Faster rendering ✔ Validation per step
✅ 8. Memoize heavy components
For example, expensive dropdowns.
xxxxxxxxxxconst CountrySelect = memo(function CountrySelect() { return <select>...</select>;});RHF already minimizes re-renders, but memoization helps further.
🧪 9. For huge forms → Use RHF + Suspense + lazy-loaded sections
If your sections are large:
xxxxxxxxxxconst WorkInfo = lazy(() => import("./WorkInfo"));Better performance for enterprise apps.
理解上面的内容之后,就可以背诵答案了:
When I deal with a large form that has 20+ fields, I mainly focus on keeping it manageable and avoiding unnecessary re-renders.
First, I usually split the form into smaller sections, like grouping related fields into separate components. This makes both the code and the UI easier to handle.
For state management, I prefer using React Hook Form instead of managing everything with
useState. It keeps the form state centralized and avoids a lot of re-render issues, which becomes important when the form gets large.
For validation, I typically use a schema-based solution like Zod, so all the validation rules are defined in one place and stay consistent with TypeScript types.
If the form has repeated patterns, I also try to create reusable input components or even generate fields from a config object to reduce boilerplate.
And from a UX perspective, if the form feels too long, I would split it into multiple steps or tabs, so users don’t feel overwhelmed.
So overall, I try to keep it simple: split the form, centralize state, and reuse as much logic and UI as possible.
面试管想听到的要点就是:the theme state should be global, so whenever you toggle the theme at the global state, it will be auto reflected in the children level.
1、I will check whether the dark mode is user preference based or system based.
2、if it's system based.I will use prefers-color-scheme media query to detect system preference on first load.then set the theme mode.
3、if it's user mode, I often rely on react context or redux to set global state, and rely on CSS variables or tailwindcss to set dark mode.
简要回答:
To implement dark mode in a React app, I usually start by managing a theme state at a global level, so the whole app can access it. This can be done with React Context or a state library like Zustand.
Then I store the user’s preference in
localStorage, so when they refresh the page or come back later, the theme is still remembered.
For switching themes, I usually add a toggle button that updates the global theme state.
For styling, I prefer using CSS variables or a solution like Tailwind CSS. For example, with Tailwind I can use the
darkclass strategy, where I just add or remove adarkclass on the root element, and all components automatically switch styles.
So overall, it’s basically: keep a global theme state, persist it in storage, and use CSS or a styling system that can react to that theme.
XSS (Cross-Site Scripting)
Frontend apps
为什么会发生 XSS?
前端可以做什么?
可用工具 / 方法?
{userInput}dangerouslySetInnerHTML + DOMPurifyeval()To prevent XSS in frontend apps, I usually think in terms of layers of protection.
First, in React, I rely on its default behavior, because React automatically escapes values in JSX. So in most cases, XSS is already prevented by default.
The main risk comes when we use
dangerouslySetInnerHTML. I try to avoid it as much as possible. If I really need it, I sanitize the content first using something like DOMPurify to make sure no scripts can be injected.
Second, I’m careful with user-generated URLs. For example, I always validate links and only allow safe protocols like
https, so users can’t inject something likejavascript:.
Third, on the server or deployment side, I like to use a Content Security Policy. That way, even if something slips through, the browser will block unsafe scripts from running.
And finally, I also keep dependencies updated and check for known vulnerabilities, just to reduce risk from third-party libraries.
So overall, I think of it as: React handles most of it by default, I sanitize risky inputs when needed, and I use CSP as a safety net.
更短记忆版(面试快答)
React already escapes content by default, so that’s the first protection. I avoid
dangerouslySetInnerHTML, or sanitize it with DOMPurify if needed. I validate user URLs to preventjavascript:injection. I also use CSP to block unsafe scripts at the browser level. Overall, it’s about layering defenses.
1️⃣ 题目关键词
Offline/Online state
Gracefully
React
Service Workers
2️⃣ 拆解后的思路
你可以把题目拆解成三个小问题:
(A) 如何在 React 中检测在线/离线状态?
用浏览器 API:
xxxxxxxxxxnavigator.onLine // 当前是否在线window.addEventListener('online', callback)window.addEventListener('offline', callback)在 React 里可以用 useState + useEffect 或 Context 来存储全局状态。
UI 上可以显示提示:比如顶部横幅“你已经离线”。
(B) 如何用 Service Worker 支持离线功能?
Service Worker 可以缓存静态资源和 API 数据。
核心点:
推荐使用库:Workbox 或 PWA setup(Create React App 默认支持)。
(C) 如何在重新上线时同步数据?
3️⃣ 组合成回答框架
navigator.onLine + online/offline 事件老师的要点:
1、I will use navigator.onLine API provided by the browser, to detect network status. And listen to the online and offline events.
2、use service workers to cache critical assets and API responses.
To handle offline and online states in a React app, I usually combine browser APIs with a service worker.
First, on the frontend, I use
navigator.onLineand listen to theonlineandofflineevents. I store this status in a global state like Context or a store, so the whole app can react to it. For example, I can show a small banner when the user goes offline or comes back online.
Then, I use a Service Worker to make the app work offline. It helps cache static assets and sometimes API responses, so the app can still load even without network.
For more advanced cases, I can also queue user actions while offline, and when the user is back online, I sync those requests to the server.
So overall, it’s basically: detect network status on the client, update the UI, and use a service worker to provide offline caching and optional syncing.
Production is not getting the latest update
Unless in incognito
为什么普通用户看到旧版本?
为什么隐身模式正常?
如何解决?
简要回答:
This is usually a cache issue. If production works in incognito but not in a normal tab, it almost always means the browser is loading an old version from cache.
To fix it, I usually look at three things.
First, I make sure the build uses hashed file names for JS and CSS. So when the code changes, the filename changes too, and the browser is forced to download the new version.
Second, I check the caching rules for
index.html. This file should not be heavily cached, because it points to all the updated assets. So I usually set it to something likeno-cache, so the browser always checks for a new version.
Third, if the app uses a service worker, I make sure it’s handled properly. Sometimes the old service worker keeps serving cached files. In that case, I update the service worker logic so it can detect a new version and refresh or replace the old cache.
So overall, the idea is: let static assets be cached safely with hashes, but make sure the entry HTML and service worker always stay up to date.